﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace WXY.Course.Common.MVCExtension
{
    public static class EnumExtension
    {
        private static IList<SelectListItem> GetSelectList(Type type, Enum selectedValue = null)
        {
            IList<SelectListItem> selectList = GetSelectItems(type);
            foreach (SelectListItem item in selectList)
            {
                string valueString = (selectedValue == null) ? "-1" : selectedValue.ToString("d");

                item.Selected = (valueString == item.Value);
            }
            return selectList;
        }

        /// <summary>
        /// 将枚举中的值组合为List
        /// </summary>
        /// <param name="enumType"></param>
        /// <returns></returns>
        private static IList<SelectListItem> GetSelectItems(Type enumType)
        {
            IList<SelectListItem> selectList = new List<SelectListItem>();

            Type checkedType = Nullable.GetUnderlyingType(enumType) ?? enumType;
            if (checkedType != enumType)
            {
                selectList.Add(new SelectListItem { Text = String.Empty, Value = String.Empty, });
            }

            foreach (object e in Enum.GetValues(enumType))
            {
                selectList.Add(new SelectListItem()
                {
                    Text = GetDisplayName(enumType, e.ToString(), AttributeTargets.Field),
                    Value = ((int)e).ToString()
                });
            }
            return selectList;
        }

        public static MvcHtmlString DisplayName(this HtmlHelper helper, Type type, string name, AttributeTargets target)
        {
            return new MvcHtmlString(GetDisplayName(type, name, target));
        }

        /// <summary>
        /// 获取DisplayAttribute的Name值
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="propertyName">字段或属性名</param>
        /// <param name="target">应用程序元素</param>
        /// <returns></returns>
        private static string GetDisplayName(Type type, string propertyName, AttributeTargets target)
        {
            dynamic fieldInfo = null;

            switch (target)
            {
                case AttributeTargets.Field:
                    fieldInfo = type.GetField(propertyName);
                    break;
                case AttributeTargets.Property:
                    fieldInfo = type.GetProperty(propertyName);
                    break;
                case AttributeTargets.Method:
                    fieldInfo = type.GetMethod(propertyName);
                    break;
                case AttributeTargets.Parameter:
                    throw new NotSupportedException("GetDisplayName did not support parameter target");
            }

            if (fieldInfo == null) return null;//如果枚举中未定义，则为异常状态

            var attribute = Attribute.GetCustomAttribute(fieldInfo, typeof(DisplayAttribute)) as DisplayAttribute;

            return attribute == null ? null : attribute.Name;
        }

        public static MvcHtmlString EnumDropDownList(this HtmlHelper htmlHelper, string name, Type enumType)
        {
            return EnumDropDownList(htmlHelper, name, enumType, null, null);
        }

        public static MvcHtmlString EnumDropDownList(this HtmlHelper htmlHelper, string name, Type enumType, string optionLabel)
        {
            return EnumDropDownList(htmlHelper, name, enumType, optionLabel, null);
        }

        public static MvcHtmlString EnumDropDownList(this HtmlHelper htmlHelper, string name, Type enumType, string optionLabel, object htmlAttributes)
        {
            IList<SelectListItem> selectList = GetSelectList(enumType);
            return SelectExtensions.DropDownList(htmlHelper, name, selectList, optionLabel, htmlAttributes);
        }


        public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Type enumType)
        {
            return EnumDropDownListFor<TModel, TProperty>(htmlHelper, expression, enumType, null, null);
        }

        public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Type enumType, string optionLabel)
        {
            return EnumDropDownListFor<TModel, TProperty>(htmlHelper, expression, enumType, optionLabel, null);
        }

        public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Type enumType, string optionLabel, object htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

            Enum currentValue = metadata.Model as Enum;

            var selectList = GetSelectList(enumType, currentValue);

            StringBuilder listItemBuilder = BuildItems(optionLabel, selectList);

            TagBuilder tagBuilder = new TagBuilder("select")
            {
                InnerHtml = listItemBuilder.ToString()
            };

            string expressionName = ExpressionHelper.GetExpressionText(expression);

            string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionName);

            tagBuilder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
            tagBuilder.MergeAttribute("name", fullName, true);
            tagBuilder.GenerateId(fullName);

            ModelState modelState;
            if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
            {
                if (modelState.Errors.Count > 0)
                {
                    tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
                }
            }

            tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(fullName, metadata));

            return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.Normal));

        }

        private static StringBuilder BuildItems(string optionLabel, IEnumerable<SelectListItem> selectList)
        {
            StringBuilder listItemBuilder = new StringBuilder();

            // Make optionLabel the first item that gets rendered.
            if (optionLabel != null)
            {
                listItemBuilder.AppendLine(ListItemToOption(new SelectListItem()
                {
                    Text = optionLabel,
                    Value = String.Empty,
                    Selected = false
                }));
            }

            foreach (SelectListItem item in selectList)
            {
                listItemBuilder.AppendLine(ListItemToOption(item));
            }

            return listItemBuilder;
        }

        private static string ListItemToOption(SelectListItem item)
        {
            TagBuilder builder = new TagBuilder("option")
            {
                InnerHtml = HttpUtility.HtmlEncode(item.Text)
            };
            if (item.Value != null)
            {
                builder.Attributes["value"] = item.Value;
            }
            if (item.Selected)
            {
                builder.Attributes["selected"] = "selected";
            }
            return builder.ToString(TagRenderMode.Normal);
        }
    }
}
